package com.innjia.sys.controller;

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.innjia.base.annotation.SysLog;
import com.innjia.base.common.Constant;
import com.innjia.base.enums.UserSysRoleEnum;
import com.innjia.base.exception.LoginException;
import com.innjia.base.utils.Result;
import com.innjia.shiro.utils.LoginToken;
import com.innjia.sys.entity.UserEntity;
import com.innjia.sys.service.*;
import com.innjia.utils.ShiroUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 类的功能描述.
 * 登陆控制
 *
 * @author zhoux
 * @auther innjia
 * @Date 2017/4/28
 */
@Controller
public class LoginController extends BaseController {
    private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);


    @Autowired
    private Producer producer;

    @Value("${sns.code.text}")
    private String snsText;

    @Value(("${sns.code.length}"))
    private int snsLenght;

    @Resource(name = "snsInnjiaService")
    private SnsService snsService;


    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    @Resource(name = "oAuth2LoginServiceAliPay")
    private OAuth2LoginService aliapyOAuth2LoginService;

    @Resource(name = "oAuth2LoginServiceXiongAn")
    private OAuth2LoginService xiongAnOAuth2LoginService;

    @Autowired
    private UserLandlordService userLandlordService;

    @Autowired
    private UserTenantService userTenantService;

    /**
     * 验证码最大发送次数统计
     */
    @Value("${sns.innjia.code.timeout}")
    private int codeTimeout;

    /**
     * 验证码超时时间
     */
    @Value("${sns.innjia.code.time}")
    private int codeTime;

    @Value("${sns.innjia.code.max}")
    private int maxSendCount;

    @RequestMapping("/login/captcha")
    public void captcha(HttpServletResponse response) throws ServletException, IOException {
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");

        //生成文字验证码
        String text = producer.createText();
        //生成图片验证码
        BufferedImage image = producer.createImage(text);
        //保存到shiro session
        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);

        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image, "jpg", out);
    }

    @ResponseBody
    @RequestMapping(value = "/login/sns/{mobileNumber}")
    public Result sendSMS(@PathVariable String mobileNumber) {
        //验证短信是否多次发送(30分钟内只能发送10次)
        String countKey = Constant.SNS__PREFIX__MOBILE + mobileNumber;
        long sendCount = redisTemplate.opsForValue().increment(countKey, 1L);
        redisTemplate.expire(countKey, codeTimeout, TimeUnit.MINUTES);
        if (sendCount >= maxSendCount) {
            return Result.error("短信短时间内发送次数过多");
        }
        String code = getCode(mobileNumber);
        snsService.sendMessage(mobileNumber, String.format(snsText, code));
        return Result.ok();

    }

    /**
     * 获取验证码,并写入缓存(这个方法可以用在登录时模拟)
     *
     * @param mobileNumber
     * @return
     */
    private String getCode(String mobileNumber) {
        String codeKey = Constant.SNS__PREFIX__CODE + mobileNumber;
        //生成纯数字验证码(因为验证码是给手机的),发送,并记录(验证码有效期10分钟)
        String code = String.format("%0" + snsLenght + "d", (int) (Math.random() * Math.pow(10, snsLenght)));
        redisTemplate.opsForValue().set(codeKey, code);
        redisTemplate.expire(codeKey, codeTime, TimeUnit.MINUTES);
        return code;
    }

    @ResponseBody
    @RequestMapping(value = "/login/loginByOauth2", method = RequestMethod.POST)
    public Result loginByOauth2(String authCode, String loginSrc, String loginType, String uid, ServletRequest request) {
        UserSysRoleEnum roleEnum = UserSysRoleEnum.valueOf(loginType);
        UserService userService = null;
        if (UserSysRoleEnum.TENANT.equals(roleEnum)) {
            userService = userTenantService;
        } else if (UserSysRoleEnum.LANDLORD.equals(roleEnum)) {
            userService = userLandlordService;
        } else {
            throw new RuntimeException("未知的用户类型");
        }
        if (Constant.LOGINT_SRC_ALIPAY.equals(loginSrc)) {
            return alipayLoign(authCode, loginSrc, request, roleEnum, userService);
        }
        if (Constant.LOGINT_SRC_XIONGAN.equals(loginSrc)) {
            return xiongAnLogin(authCode, loginSrc, uid, request, userService);
        } else {
            throw new RuntimeException("不支持的登录类型");
        }
    }

    private Result alipayLoign(String authCode, String loginSrc, ServletRequest request, UserSysRoleEnum roleEnum, UserService userService) {
        Map<String, String> params = new HashMap<>(2);
        params.put(OAuth2LoginService.KEY_AUTH_CODE, authCode);
        Map<String, String> userInfo = aliapyOAuth2LoginService.getAccessToken(params);
        String uid = userInfo.get(OAuth2LoginService.KEN_UID);
        UserEntity userEntity = userService.queryByUid(uid, loginSrc);
        if (null != userEntity) {
            //如果账户存在,则单点登录
            Subject subject = ShiroUtils.getSubject();
            LoginToken token = LoginToken.buildApp(request.getRemoteHost(), null, null, roleEnum, uid, loginSrc);
            token.setUserEntity(userEntity);
            subject.login(token);
            Result result = Result.ok();
            result.put("token", token.getToken());
            result.put("userInfo", ShiroUtils.getUserEntity());
            return result;
        } else {
            Result result = Result.error("您尚未认证，请前往雄安新区公共租赁办事处认证");
            result.put("uid", uid);
            result.put("loginSrc", loginSrc);
            return result;
        }
    }

    private Result xiongAnLogin(String authCode, String loginSrc, String uid, ServletRequest request, UserService userService) {
        UserEntity userEntity = userService.queryByUid(uid, loginSrc);
        if (null == userEntity) {
            Map<String, String> tokenParams = new HashMap<>(2);
            tokenParams.put(OAuth2LoginService.KEY_AUTH_CODE, authCode);
            tokenParams.put(OAuth2LoginService.KEN_UID, uid);
            //获取token
            Map<String, String> tokenMap = xiongAnOAuth2LoginService.getAccessToken(tokenParams);
            Map<String, String> userParams = new HashMap<>(2);
            userParams.put(OAuth2LoginService.KEN_TOKEN, tokenMap.get(OAuth2LoginService.KEN_TOKEN));
            //获取用户信息
            userEntity = xiongAnOAuth2LoginService.getUserInfo(userParams);
        }
        String code = getCode(userEntity.getPhone());
        //直接调用登录方法执行登录(登录方法租客如不存在则新增(雄安APP这里只可能是租客))
        return login(null, null, userEntity.getPhone(), code, UserSysRoleEnum.ROLE_TENANT, null, false, uid, Constant.LOGINT_SRC_XIONGAN, request);
    }

    /**
     * 登录
     *
     * @param username
     * @param password
     * @param phone
     * @param code
     *         短信验证码
     * @param loginType
     *         登录类型 UserSysRoleEnum
     * @param captcha
     *         验证码
     * @param isRememberMe
     *         是否记住我
     * @param uid
     *         单点登录用户ID
     * @param loginSrc
     *         单点登录,账号来源
     * @return
     * @throws IOException
     */
    @ResponseBody
    @RequestMapping(value = "/login/login", method = RequestMethod.POST)
    public Result login(String username, String password, String phone, String code, String loginType, String captcha, boolean isRememberMe, String uid, String loginSrc, ServletRequest request) {
        UserSysRoleEnum roleEnum;
        //默认后台登录
        if (null == loginType) {
            roleEnum = UserSysRoleEnum.ADMIN;
        } else {
            roleEnum = UserSysRoleEnum.valueOf(loginType);
        }
        try {
            if (UserSysRoleEnum.ADMIN.equals(roleEnum)) {
                return adminLogin(username, password, captcha, isRememberMe, request);
            } else if (UserSysRoleEnum.LANDLORD.equals(roleEnum) || UserSysRoleEnum.TENANT.equals(roleEnum)) {
                return appLogin(phone, code, uid, loginSrc, request, roleEnum);
            } else {
                return Result.error("不支持的登录类型");
            }
        } catch (LoginException e) {
            LOGGER.debug("", e);
            if (null != e.getResult()) {
                return e.getResult();
            } else {
                return Result.error("账号或密码不正确");
            }
        }
    }

    private Result adminLogin(String username, String password, String captcha, boolean isRememberMe, ServletRequest request) {
        String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
        //kaptcha
        if (null == kaptcha) {
            return Result.error("验证码超时");
        }
        if (!captcha.equalsIgnoreCase(kaptcha)) {
            return Result.error("验证码不正确");
        }
        Subject subject = ShiroUtils.getSubject();
        LoginToken token = LoginToken.buildAdmin(username, password, isRememberMe, request.getRemoteHost());
        subject.login(token);
        return Result.ok();
    }

    private Result appLogin(String phone, String code, String uid, String loginSrc, ServletRequest request, UserSysRoleEnum roleEnum) {
        Subject subject = ShiroUtils.getSubject();
        LoginToken token = LoginToken.buildApp(request.getRemoteHost(), phone, code, roleEnum, uid, loginSrc);
        subject.login(token);
        Result result = Result.ok();
        result.put("token", token.getToken());
        result.put("userInfo", ShiroUtils.getUserEntity());
        return result;
    }

    /**
     * 方法logOut的功能描述:
     * 退出登陆
     *
     * @return java.lang.String
     * @params []
     * @auther innjia
     * @date 2017-05-02 14:01:23
     */
    @RequestMapping(value = "logout", method = RequestMethod.GET)
    @SysLog("退出系统")
    public String logOut() {
        //删除session
        ShiroUtils.logout();
        return "redirect:/login.html";
    }


}
